package com.yvan.serverless;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.yvan.YvanUtil;
import com.yvan.platform.JsonWapper;
import com.yvan.serverless.mvc.Model;
import com.yvan.serverless.mvc.ModelOps;
import com.yvan.serverless.utils.FileUtilExt;
import com.yvan.serverless.utils.Tuple;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;

import java.io.File;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class DesignService {

    private final ServerLessProperties properties;

    public DesignService(ServerLessProperties properties) {
        this.properties = properties;
    }

    /**
     * 读取应用配置信息
     */
    Model<Map<String, Object>> appInfo() {
        YvanUIDesignProperties design = properties.getDesign();
        if (design == null || Strings.isNullOrEmpty(design.getUrl())) {
            throw new RuntimeException("该应用不允许修改");
        }

        Map<String, Object> ret = Maps.newLinkedHashMap();
        ret.put("needAuth", !Strings.isNullOrEmpty(design.getAuth()));

        return Model.newSuccess(ret);
    }


    /**
     * 获取模块的所有内容
     */
    Model<Object> moduleContent(String modulePath) {
        YvanUIDesignProperties design = properties.getDesign();
        String path;
        String type;
        if (modulePath.endsWith("view.ts")) {
            path = FileUtilExt.normalizePath(design.getClientSrcLocation(), modulePath);
            type = "view";

        } else if(modulePath.endsWith(".ts")) {
            path = FileUtilExt.normalizePath(design.getClientSrcLocation(), modulePath);
            type = "ts";

        } else if(modulePath.endsWith(".entity.xml")
                || modulePath.endsWith(".database.xml")) {
            path = FileUtilExt.normalizePath(design.getModelSrcLocation(), modulePath);
            type = "model";

        } else {
            path = FileUtilExt.normalizePath(design.getBpmSrcLocation(), modulePath);
            type = "bpmn";
        }
        File file = new File(path);
        if (!file.exists()) {
            throw new RuntimeException("非法的模块" + modulePath);
        }

        String content = FileUtilExt.readContent(file);

        Map<String, Object> fileContentMap = Maps.newLinkedHashMap();
        fileContentMap.put("path", modulePath);
        fileContentMap.put("type", type);
        fileContentMap.put("content", content);
        fileContentMap.put("lastModifyTime",file.lastModified() + "");
        return Model.newSuccess(fileContentMap);
    }


    /**
     * 获取模块service端的所有内容
     */
    Model<Object> moduleServiceContent(String modulePath) {
        YvanUIDesignProperties design = properties.getDesign();

        String serivcePath = FileUtilExt.normalizePath(design.getServerSrcLocation(), modulePath + "Service.ts");
        String sqlPath = FileUtilExt.normalizePath(design.getServerSrcLocation(), modulePath + ".xml");
        File serviceFile = new File(serivcePath);
        File sqlFile = new File(sqlPath);
        if (!serviceFile.exists() || !sqlFile.exists()) {
            throw new RuntimeException("非法的模块" + modulePath);
        }

        String serviceContent = FileUtilExt.readContent(serviceFile);
        String sqlContent = FileUtilExt.readContent(sqlFile);

        Map<String, Object> fileContentMap = Maps.newLinkedHashMap();
        fileContentMap.put("serviceContent", serviceContent);
        fileContentMap.put("sqlContent", sqlContent);

        return Model.newSuccess(fileContentMap);
    }

    /**
     * 获取文件的最后更新时间
     * @param modulePath
     * @return
     */
    Model moduleCheck(String modulePath) {
        YvanUIDesignProperties design = properties.getDesign();
        String path;
        if (modulePath.endsWith("view.ts")) {
            path = FileUtilExt.normalizePath(design.getClientSrcLocation(), modulePath);

        } else if(modulePath.endsWith(".ts")) {
            path = FileUtilExt.normalizePath(design.getClientSrcLocation(), modulePath);

        } else if(modulePath.endsWith(".entity.xml")
                || modulePath.endsWith(".database.xml")) {
            path = FileUtilExt.normalizePath(design.getModelSrcLocation(), modulePath);

        } else {
            path = FileUtilExt.normalizePath(design.getBpmSrcLocation(), modulePath);
        }
        File file = new File(path);
        if (!file.exists()) {
            throw new RuntimeException("非法的模块" + modulePath);
        }

        Map<String, String> re = new LinkedHashMap<>();
        re.put("path",modulePath);
        re.put("lastModifyTime",file.lastModified() + "");
        return Model.newSuccess(re);
    }

    /**
     * 保存模块内容
     * <pre>
     * {
     *   path: /a/b/c.view.ts
     *   content: "xxxxxx"
     * }
     * </pre>
     */
    Model moduleSave(JsonWapper jw) {
        String modulePath = jw.asStr("path");
        checkModulePathValid(modulePath);

        var tp = getParentDirAndSimpleName(modulePath);
        var parentDir = tp.v1();
        var moduleSimpleName = tp.v2();
        String path = parentDir.getAbsolutePath() + "/" + moduleSimpleName;
        YvanUtil.saveToFile(path, jw.asStr("content"));
        File file = new File(path);
        if (!file.exists()) {
            throw new RuntimeException("非法的模块" + modulePath);
        }

        Map<String, String> re = Maps.newLinkedHashMap();
        re.put("path",modulePath);
        re.put("lastModifyTime",file.lastModified() + "");
        return Model.newSuccess(re).setMsg("保存成功!");
    }

    /**
     * 保存模块内容
     * <pre>
     * {
     *   path: /a/b/c.view.ts
     *   content: "xxxxxx"
     * }
     * </pre>
     */
    ModelOps moduleServiceSave(JsonWapper jw) {
        String modulePath = jw.asStr("path");
        YvanUIDesignProperties design = properties.getDesign();
        String serivcePath = FileUtilExt.normalizePath(design.getServerSrcLocation(), modulePath + "Service.ts");
        String sqlPath = FileUtilExt.normalizePath(design.getServerSrcLocation(), modulePath + ".xml");
        File serviceFile = new File(serivcePath);
        File sqlFile = new File(sqlPath);
        if (!serviceFile.exists() || !sqlFile.exists()) {
            throw new RuntimeException("非法的模块" + modulePath);
        }

        YvanUtil.saveToFile(serivcePath, jw.asStr("serviceContent"));
        YvanUtil.saveToFile(sqlPath, jw.asStr("sqlContent"));
        return ModelOps.newSuccess();
    }

    Tuple<File, String> getParentDirAndSimpleName(String modulePath) {
        YvanUIDesignProperties design = this.properties.getDesign();
        String jsonPath = "";
        if (modulePath.endsWith(".ts")) {
            jsonPath = FileUtilExt.normalizePath(design.getClientSrcLocation(), modulePath);
        } else if (modulePath.endsWith("bpmn")) {
            jsonPath = FileUtilExt.normalizePath(design.getBpmSrcLocation(), modulePath);
        } else if (modulePath.endsWith(".entity.xml")
                || modulePath.endsWith(".database.xml")) {
            jsonPath = FileUtilExt.normalizePath(design.getModelSrcLocation(), modulePath);
        }
        File parentDir = new File(jsonPath.substring(0, jsonPath.lastIndexOf("/")));
        String moduleSimpleName = modulePath.substring(modulePath.lastIndexOf("/") + 1);
        return new Tuple<>(parentDir, moduleSimpleName);
    }

    void checkModulePathValid(String modulePath) {
        if (!modulePath.startsWith("/")) {
            throw new RuntimeException("模块名必须以 / 开头");
        }
        if (!modulePath.endsWith(".ts")
                && !modulePath.endsWith(".bpmn")
                && !modulePath.endsWith(".entity.xml")
                && !modulePath.endsWith(".database.xml")) {
            throw new RuntimeException("模块名必须以 .ts 或 bpmn 或 .entity.xml 或 .database.xml 结尾");
        }
    }

    /**
     * 获取所有模块的树形结构列表
     */
    Model<List<Map<String, Object>>> moduleTree() {
        YvanUIDesignProperties design = properties.getDesign();
        List<Map<String, Object>> result = Lists.newArrayList();

        //client
        if (!Strings.isNullOrEmpty(design.getClientSrcLocation())) {
            String path = FileUtilExt.normalizePath(design.getClientSrcLocation());
            File file = new File(path);
            moduleTreeTrav(result, file, "");
        }

        //bpm
        if (!Strings.isNullOrEmpty(design.getBpmSrcLocation())) {
            String path = FileUtilExt.normalizePath(design.getBpmSrcLocation());
            File file = new File(path);
            moduleTreeTrav(result, file, "");
        }

        //xml
        if (!Strings.isNullOrEmpty(design.getModelSrcLocation())) {
            String path = FileUtilExt.normalizePath(design.getModelSrcLocation());
            File file = new File(path);
            moduleTreeTrav(result, file, "");
        }

        return Model.newSuccess(result);
    }

    void moduleTreeTrav(List<Map<String, Object>> list, File dir, String parentLocation) {
        File[] files = dir.listFiles();
        if (files == null || files.length<=0) {
            return;
        }
        for (File f : files) {
            if (f.isDirectory()) {
                moduleTreeTrav(list, f, parentLocation + "/" + f.getName());
            } else {
                String nameLow = f.getName().toLowerCase();
                if (nameLow.endsWith(".view.ts")) {
                    Map<String, Object> row = Maps.newLinkedHashMap();

                    row.put("path", parentLocation + "/" + f.getName());
                    row.put("name", f.getName());
                    row.put("type", "view");
                    row.put("lastModified", f.lastModified());

                    list.add(row);
                } else if (nameLow.endsWith(".bpmn")) {
                    Map<String, Object> row = Maps.newLinkedHashMap();

                    row.put("path", parentLocation + "/" + f.getName());
                    row.put("name", f.getName());
                    row.put("type", "bpmn");
                    row.put("lastModified", f.lastModified());

                    list.add(row);
                } else if (nameLow.endsWith(".entity.xml")
                        || nameLow.endsWith(".database.xml")) {
                    Map<String, Object> row = Maps.newLinkedHashMap();

                    row.put("path", parentLocation + "/" + f.getName());
                    row.put("name", f.getName());
                    row.put("type", "model");
                    row.put("lastModified", f.lastModified() + "");

                    list.add(row);
                }
            }
        }
    }

    public Object moduleCreate(JsonWapper jw) {
        String moduleType = jw.asStr("type");
        String modulePath = jw.asStr("path");
        String moduleName = jw.asStr("name");
        String moduleContent = jw.asStr("content");
        String moduleViewContent = jw.asStr("viewContent");
        String serviceContent = jw.asStr("serviceContent");
        String sqlContent = jw.asStr("sqlContent");
        YvanUIDesignProperties design = properties.getDesign();

        String fileName = "";
        String fileViewName= "" ;
        String fileFullPath = "";
        String fileViewFullPath = "";

        String sqlFileName= "" ;
        String sqlFileFullPath = "";
        String serviceFileName = "";
        String serviceFileFullPath= "";
        if (moduleType.equals("module") || moduleType.equals("dialog") || moduleType.equals("widget")) {
            fileName = moduleName + ".ts";
            fileViewName = moduleName + ".view.ts";
            fileFullPath = FileUtilExt.normalizePath(design.getClientSrcLocation(), modulePath, fileName);
            fileViewFullPath = FileUtilExt.normalizePath(design.getClientSrcLocation(), modulePath, fileViewName);

            sqlFileName = moduleName + ".xml";
            sqlFileFullPath = FileUtilExt.normalizePath(design.getServerSrcLocation(), modulePath, sqlFileName);
            serviceFileName = moduleName + "Service.ts";
            serviceFileFullPath = FileUtilExt.normalizePath(design.getServerSrcLocation(), modulePath, serviceFileName);

        }else if (moduleType.equals("bpmn")){
            fileName = moduleName + ".bpmn";
            fileFullPath = FileUtilExt.normalizePath(design.getBpmSrcLocation(), modulePath, fileName);
            moduleViewContent ="";
            serviceContent = "";
            sqlContent = "";

        }
        else {
            return ModelOps.newFail("无效的 type 类型");
        }

        File f1 = new File(fileFullPath);
        File f2 = new File(fileViewFullPath);
        File f3 = new File(sqlFileFullPath);
        File f4 = new File(serviceFileFullPath);
        if (f1.exists() || f2.exists() || f3.exists() || f4.exists()) {
            return ModelOps.newFail("路径'" + modulePath + "'已经存在！");
        }
        if (!Strings.isNullOrEmpty(fileFullPath) && !f1.getParentFile().exists()) {
            f1.getParentFile().mkdirs();
        }
        if (!Strings.isNullOrEmpty(fileViewFullPath) &&!f2.getParentFile().exists()) {
            f2.getParentFile().mkdirs();
        }
        if (!Strings.isNullOrEmpty(sqlFileFullPath) &&!f3.getParentFile().exists()) {
            f3.getParentFile().mkdirs();
        }
        if (!Strings.isNullOrEmpty(serviceFileFullPath) &&!f4.getParentFile().exists()) {
            f4.getParentFile().mkdirs();
        }

        YvanUtil.saveToFile(fileFullPath, moduleContent);
        if(!Strings.isNullOrEmpty(moduleViewContent)) {
            YvanUtil.saveToFile(fileViewFullPath, moduleViewContent);
        }
        if(!Strings.isNullOrEmpty(sqlContent)) {
            YvanUtil.saveToFile(sqlFileFullPath, sqlContent);
        }
        if(!Strings.isNullOrEmpty(serviceContent)) {
            YvanUtil.saveToFile(serviceFileFullPath, serviceContent);
        }
        ModelOps modelOps = ModelOps.newSuccess();
        modelOps.setMsg("添加成功").setData(modulePath);
        return modelOps;
    }

    @SneakyThrows
    public Object moduleDelete(String modulePath) {
        YvanUIDesignProperties design = properties.getDesign();
        String fullPath = FileUtilExt.normalizePath(design.getClientSrcLocation(), modulePath);
        File jsonPathFile = new File(fullPath);
        if (!jsonPathFile.exists()) {
            throw new RuntimeException("文件不存在");
        }
        if (jsonPathFile.isDirectory()) {
            //删除目录
            log.info("delete Directory " + jsonPathFile.getAbsolutePath());
            org.apache.commons.io.FileUtils.deleteDirectory(jsonPathFile);
            return ModelOps.newSuccess();
        }

        checkModulePathValid(modulePath);

        val tp = getParentDirAndSimpleName(modulePath);
        val parentDir = tp.v1();
        val moduleSimpleName = tp.v2();
        File[] files = parentDir.listFiles(f -> f.isFile() && f.getName().startsWith(moduleSimpleName) && f.getName().endsWith(".js"));
        if (files != null) {
            for (File f : files) {
                log.info("delete " + f.getAbsolutePath());
                f.delete();
            }
        }
        File jsonFile = new File(FileUtilExt.normalizePath(parentDir.getAbsolutePath(), moduleSimpleName));
        log.info("delete " + jsonFile.getAbsolutePath());
        jsonFile.delete();

        return ModelOps.newSuccess();
    }
}
